package com.maixy.redisdemo.redis.template;

import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 *
 * Created by jason on 17-4-8.
 */
@Component
public class ZsetRedisTemplate extends BaseRedisTemplate {

    private static final String EMPTY_MEMBER = "-1";
    private static final double EMPTY_SCORE = -1;
    private static final int MAX_PAGE = 999;

    @Resource(name = "redisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    public void zadd(String key, String member, double score) {
        redisTemplate.opsForZSet().add(key, member, score);
    }

    public void zaddEmpty(String key) {
        redisTemplate.opsForZSet().add(key, EMPTY_MEMBER, EMPTY_SCORE);
    }

    public void zadd(String key, Set<ZSetOperations.TypedTuple<String>> set) {
        redisTemplate.opsForZSet().add(key, set);
    }

    public <T> List<T> zrevrangeByScore(String key, double min, double max, long offset, long count, Class<T> clazz) {
        List<T> results = new ArrayList<>();
        Set<String> vals = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
        if (!CollectionUtils.isEmpty(vals)) {
            for(String val : vals) {
                T obj = JSON.parseObject(val, clazz);
                results.add(obj);
            }
        }
        return results;
    }

    public Set<String> zrevrangeByScore(String key, double min, double max, long offset, long count) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
    }

    /**
     * 正序
     */
    public Set<String> rangeByScore(String key, double min, double max, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
    }

    public List<String> zrevrangeByScoreForList(String key, double min, double max, long offset, long count) {
        Set<String> strs =  redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
        if(strs.size() > 0) {
            List<String> result = new ArrayList<>();
            for(String str : strs) {
                result.add(str);
            }
            return result;
        } else {
            return null;
        }
    }

    public List<String> zrangeByScoreForList(String key, double min, double max) {
        Set<String> strs =  redisTemplate.opsForZSet().rangeByScore(key, min, max);
        if(strs.size() > 0) {
            List<String> result = new ArrayList<>();
            for(String str : strs) {
                result.add(str);
            }
            return result;
        } else {
            return null;
        }
    }

    public List<String> zrangeByScoreForList(String key, double min, double max, long offset, long count) {

        Set<String> strs =  redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
        if(strs.size() > 0) {
            List<String> result = new ArrayList<>();
            for(String str : strs) {
                result.add(str);
            }
            return result;
        } else {
            return null;
        }
    }

    public Set<String> zrevrangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    public void zrem(String key, Object member) {
        redisTemplate.opsForZSet().remove(key, member);
    }

    public long zcount(String key, long min, long max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    public long zcard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    public Double zscore(String key, String member) {
        return redisTemplate.opsForZSet().score(key, member);
    }

    public Long zrank(String key, String member) {
        return redisTemplate.opsForZSet().rank(key, member);
    }

    public Long zrevrank(String key, String member) {
        return redisTemplate.opsForZSet().reverseRank(key, member);
    }

    public Set<String> zrevrange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    public void incrBy(String key, String member, Double num) {
        redisTemplate.opsForZSet().incrementScore(key, member, num);
    }

    public Set<ZSetOperations.TypedTuple<String>> reverseRangeByScoreWithScores(String key, double min, double max, long offset, long count) {
        Set<ZSetOperations.TypedTuple<String>> results = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max, offset, count);
        return CollectionUtils.isEmpty(results) ? Collections.emptySet() : results;
    }

    /**
     * 提供给业务方法使用
     * 处理zset list的添加
     * @param key
     * @param businessId
     * @param time
     */
    public void handleAddCache(String key, String businessId, Long time) {
        boolean exists = exists(key);
        if (exists) {
            time = null == time ? System.currentTimeMillis() : time;
            this.zadd(key, businessId, time);
        }
    }

    /**
     * 提供给业务方法使用
     * 处理zset list的异常
     * @param key
     * @param businessId
     */
    public void handleRemCache(String key, String businessId) {
        boolean exists = exists(key);
        if (exists) {
            this.zrem(key, businessId);
        }
    }

    /**
     * 判断缓存中成员是否存在
     *
     * @param key 缓存键
     * @param member 成员
     * @return boolean
     * @author HuangLinWei
     * @since 2019/6/21
     */
    public boolean isMember(String key, String member){
        Long rank = redisTemplate.opsForZSet().rank(key, member);
        return !(rank == null || rank <= 0);
    }

    /**
     *
     * @param key 缓存key
     * @param member 成员值
     * @return
     */
    public boolean existMember(String key, String member) {
        Long rank = redisTemplate.opsForZSet().rank(key, member);
        return null != rank;
    }

    /**
     * 根据分数倒序获取全部元素
     *
     * @param key 缓存键
     * @return java.util.List<java.lang.String>
     * @author HuangLinWei
     * @since 2019/8/8
     */
    public List<String> reverseRangeAll(String key) {
        int page = 1;
        int pageSize = 100;
        List<String> resultList = new ArrayList<>(100);
        while (page < MAX_PAGE) {
            int offset = (page - 1) * pageSize;
            List<String> strings = this.zrevrangeByScoreForList(key, Long.MIN_VALUE, Long.MAX_VALUE, offset, pageSize);
            if (CollectionUtils.isEmpty(strings)) {
                break;
            }
            resultList.addAll(strings);
            page++;
        }
        return resultList;
    }

    /**
     * 根据分数升序获取全部元素
     *
     * @param key 缓存键
     * @return java.util.List<java.lang.String>
     * @author HuangLinWei
     * @since 2019/8/8
     */
    public List<String> rangeAll(String key) {
        int page = 1;
        int pageSize = 100;
        List<String> resultList = new ArrayList<>(100);
        while (page < MAX_PAGE) {
            int offset = (page - 1) * pageSize;
            List<String> strings = this.zrangeByScoreForList(key, Long.MIN_VALUE, Long.MAX_VALUE, offset, pageSize);
            if (CollectionUtils.isEmpty(strings)) {
                break;
            }
            resultList.addAll(strings);
            page++;
        }
        return resultList;
    }
}
